from typing import Tuple

import wx

from utils import switch
from .preview_panel import PreviewPanel


class ImageMarkPanel(PreviewPanel):
    source_image: wx.Image
    view_image: wx.Image
    _rsd: int = 6

    def __init__(self, parent):
        super().__init__(parent)
        self.SetWindowStyle(wx.BORDER_SUNKEN)
        self.start_x = 0
        self.start_y = 0
        self.finish_x = 0
        self.finish_y = 0

        self.current_x = 0
        self.current_y = 0

        self.actual_start_x = 0
        self.actual_start_y = 0
        self.actual_finish_x = 0
        self.actual_finish_y = 0

        self.moving_start_x = 0
        self.moving_start_y = 0
        self.__capture = False
        self.__moving = False
        self.__resizing = False
        # 以0为中心，12点位置为1，顺时针编8个方向，奇数为垂直和水平，偶数为对角
        self.__resize_direction = 0
        self.selected = False

        self.__show_amming = False
        self.aimming_pen = wx.Pen(wx.Colour(0, 255, 0), width=1, style=wx.PENSTYLE_DOT_DASH)

        self.Bind(wx.EVT_PAINT, self.__paint)
        self.Bind(wx.EVT_LEFT_DOWN, self.__start_capture)
        self.Bind(wx.EVT_LEFT_UP, self.__end_capture)
        self.Bind(wx.EVT_MOTION, self.__moving_capture)

    @property
    def crop_info(self):
        if not hasattr(self, "view_image") or not self.selected:
            return 0, 0, 0
        panel_size = self.GetSize()
        view_start = ((panel_size.GetWidth() - self.view_image.GetWidth()) // 2,
                      (panel_size.GetHeight() - self.view_image.GetHeight()) // 2)
        scale_ratio = self.view_image.GetWidth() / self.source_image.GetWidth()
        return (round((self.actual_start_x - view_start[0]) / scale_ratio),
                round((self.actual_start_y - view_start[1]) / scale_ratio),
                round((self.actual_finish_x - self.actual_start_x) / scale_ratio),
                round((self.actual_finish_y - self.actual_start_y) / scale_ratio))

    def clear_image(self):
        self.__set_resizing_mode()
        self.selected = False
        self.start_x = 0
        self.start_y = 0
        self.finish_x = 0
        self.finish_y = 0
        if hasattr(self, "source_image"):
            del self.source_image
        if hasattr(self, "view_image"):
            del self.view_image
        self.Refresh()

    def load_image(self, path: str = "", crop: Tuple[int, int, int, int] = (0, 0, 0, 0)):
        super().load_image(path)
        self.__set_resizing_mode()
        if crop[2] == 0:
            self.selected = False
        else:
            self.selected = True
            panel_size = self.GetSize()
            if not hasattr(self, "view_image"):
                scale_width, scale_height = self._calculate_scale()
                self.view_image = self.source_image.Scale(scale_width,
                                                          scale_height,
                                                          wx.IMAGE_QUALITY_HIGH)
            scale_ratio = self.view_image.GetWidth() / self.source_image.GetWidth()
            view_start = ((panel_size.GetWidth() - self.view_image.GetWidth()) // 2,
                          (panel_size.GetHeight() - self.view_image.GetHeight()) // 2)
            self.start_x = round(view_start[0] + crop[0] * scale_ratio)
            self.start_y = round(view_start[1] + crop[1] * scale_ratio)
            self.finish_x = self.start_x + round(crop[2] * scale_ratio)
            self.finish_y = self.start_y + round(crop[3] * scale_ratio)

    def select_all(self):
        if hasattr(self, "source_image") and hasattr(self, "view_image"):
            panel_size = self.GetSize()
            self.start_x = (panel_size.GetWidth() - self.view_image.GetWidth()) // 2
            self.start_y = (panel_size.GetHeight() - self.view_image.GetHeight()) // 2
            self.finish_x = self.start_x + min([panel_size.GetWidth(), self.view_image.GetWidth()])
            self.finish_y = self.start_y + min([panel_size.GetHeight(), self.view_image.GetHeight()])
            self.selected = True
        else:
            self.selected = False
        self.Refresh()

    def enable_amming(self, state=False):
        self.__show_amming = state
        self.Refresh()

    def __start_capture(self, e: wx.MouseEvent):
        self.moving_start_x, self.moving_start_y = e.GetPosition()
        if self.selected and self.__resize_direction > 0:
            self.__resizing = True
        elif self.selected and \
                (self.actual_start_x + self._rsd <= self.moving_start_x <= self.actual_finish_x - self._rsd) and \
                (self.actual_start_y + self._rsd <= self.moving_start_y <= self.actual_finish_y - self._rsd):
            self.__moving = True
        else:
            self.__capture = True
            self.selected = False
            self.SetCursor(wx.Cursor(wx.CURSOR_CROSS))
            self.start_x, self.start_y = e.GetPosition()
            self.finish_x, self.finish_y = e.GetPosition()

    def __end_capture(self, e: wx.MouseEvent):
        if self.__resizing:
            self.__resizing = False
        if self.__moving:
            self.__moving = False
        if self.__capture:
            self.__capture = False
            self.selected = True
            self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
            self.finish_x, self.finish_y = e.GetPosition()
            self.Refresh()

    def __set_resizing_mode(self, cursor=wx.CURSOR_ARROW, direction=0):
        self.SetCursor(wx.Cursor(cursor))
        self.__resize_direction = direction

    def __move_edge(self, pos: str, distance: int):
        for case in switch(pos.lower()):
            if case("top"):
                if self.start_y < self.finish_y:
                    self.start_y += distance
                else:
                    self.finish_y += distance
            if case("right"):
                if self.start_x < self.finish_x:
                    self.finish_x += distance
                else:
                    self.start_x += distance
            if case("down"):
                if self.start_y < self.finish_y:
                    self.finish_y += distance
                else:
                    self.start_y += distance
            if case("left"):
                if self.start_x < self.finish_x:
                    self.start_x += distance
                else:
                    self.finish_x += distance

    def __moving_capture(self, e: wx.MouseEvent):
        self.current_x, self.current_y = e.GetPosition()
        if e.Dragging():
            if self.__moving:
                move_length_x = self.current_x - self.moving_start_x
                move_length_y = self.current_y - self.moving_start_y
                self.start_x += move_length_x
                self.finish_x += move_length_x
                self.start_y += move_length_y
                self.finish_y += move_length_y
                self.moving_start_x, self.moving_start_y = e.GetPosition()
            elif self.__resizing:
                move_length_x = self.current_x - self.moving_start_x
                move_length_y = self.current_y - self.moving_start_y
                for case in switch(self.__resize_direction):
                    if case(1) or case(2) or case(8):
                        self.__move_edge("top", move_length_y)
                    if case(2) or case(3) or case(4):
                        self.__move_edge("right", move_length_x)
                    if case(4) or case(5) or case(6):
                        self.__move_edge("down", move_length_y)
                    if case(6) or case(7) or case(8):
                        self.__move_edge("left", move_length_x)
                self.moving_start_x, self.moving_start_y = e.GetPosition()
            else:
                self.finish_x, self.finish_y = e.GetPosition()
        else:
            if self.selected:
                if self.actual_start_x + self._rsd < self.current_x < self.actual_finish_x - self._rsd and \
                        self.actual_start_y - self._rsd <= self.current_y <= self.actual_start_y + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZENS, 1)
                elif self.actual_start_x + self._rsd < self.current_x < self.actual_finish_x - self._rsd and \
                        self.actual_finish_y - self._rsd <= self.current_y <= self.actual_finish_y + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZENS, 5)
                elif self.actual_start_y + self._rsd < self.current_y < self.actual_finish_y - self._rsd and \
                        self.actual_start_x - self._rsd <= self.current_x <= self.actual_start_x + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZEWE, 7)
                elif self.actual_start_y + self._rsd < self.current_y < self.actual_finish_y - self._rsd and \
                        self.actual_finish_x - self._rsd <= self.current_x <= self.actual_finish_x + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZEWE, 3)
                elif self.actual_start_x - self._rsd < self.current_x < self.actual_start_x + self._rsd and \
                        self.actual_start_y - self._rsd < self.current_y < self.actual_start_y + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZENWSE, 8)
                elif self.actual_finish_x - self._rsd < self.current_x < self.actual_finish_x + self._rsd and \
                        self.actual_start_y - self._rsd < self.current_y < self.actual_start_y + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZENESW, 2)
                elif self.actual_start_x - self._rsd < self.current_x < self.actual_start_x + self._rsd and \
                        self.actual_finish_y - self._rsd < self.current_y < self.actual_finish_y + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZENESW, 6)
                elif self.actual_finish_x - self._rsd < self.current_x < self.actual_finish_x + self._rsd and \
                        self.actual_finish_y - self._rsd < self.current_y < self.actual_finish_y + self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZENWSE, 4)
                elif self.actual_start_x + self._rsd < self.current_x < self.actual_finish_x - self._rsd and \
                        self.actual_start_y + self._rsd < self.current_y < self.actual_finish_y - self._rsd:
                    self.__set_resizing_mode(wx.CURSOR_SIZING, 0)
                else:
                    self.__set_resizing_mode()
        self.Refresh()

    def __paint(self, _):
        dc = wx.PaintDC(self)
        dc.SetBackground(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)))
        dc.Clear()

        panel_size = self.GetSize()
        if hasattr(self, "source_image"):
            if not hasattr(self, "view_image"):
                scale_width, scale_height = self._calculate_scale()
                self.view_image = self.source_image.Scale(scale_width,
                                                          scale_height,
                                                          wx.IMAGE_QUALITY_HIGH)
            view_image_size = self.view_image.GetSize()
            if view_image_size.GetHeight() == panel_size.GetHeight() or \
                    view_image_size.GetWidth() == panel_size.GetWidth():
                dc.DrawBitmap(self.view_image.ConvertToBitmap(),
                              (panel_size.GetWidth() - view_image_size.GetWidth()) // 2,
                              (panel_size.GetHeight() - view_image_size.GetHeight()) // 2)
        if self.__show_amming:
            horizontal_aimming = (0, self.current_y, panel_size.GetWidth(), self.current_y)
            vertical_aimming = (self.current_x, 0, self.current_x, panel_size.GetHeight())
            dc.DrawLineList([horizontal_aimming, vertical_aimming], self.aimming_pen)

        if self.selected or self.__capture:
            dc.SetBrush(wx.Brush(wx.Colour(0, 255, 0, 60)))
            dc.SetPen(wx.Pen(wx.Colour(0, 255, 35, 120)))
            self.actual_start_x = min([self.start_x, self.finish_x])
            self.actual_start_y = min([self.start_y, self.finish_y])
            self.actual_finish_x = max([self.start_x, self.finish_x])
            self.actual_finish_y = max([self.start_y, self.finish_y])
            width = self.actual_finish_x - self.actual_start_x
            height = self.actual_finish_y - self.actual_start_y
            dc.DrawRectangle(self.actual_start_x, self.actual_start_y, width, height)
